package com.ice.framework.web.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ice.framework.common.annotation.AutoResult;
import com.ice.framework.common.base.ResponseResult;
import com.ice.framework.common.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 处理RestController返回值问题
 * 当请求体中出现了HttpServletResponse时，同时返回值 = null,则不会走该段逻辑，是因为在转化参数时，ServletResponseMethodArgumentResolver已经将requestHandled设置成了true，返回值未null时，直接返回
 * @AutoResult
 * @PostMapping("/test")
 * public void test(HttpServletResponse response) {
 *     System.out.println("测试返回内容");
 * }
 *  此时返回的内容什么都没有，而不是输出  {"errorCode": "0","errorMsg": "成功" }
 * @author wangwei
 * @Date 2021/12/22 17:06
 */
public class AutoResultReturnValueHandler implements HandlerMethodReturnValueHandler {

    private static Logger logger = LoggerFactory.getLogger(AutoResultReturnValueHandler.class);

    public AutoResultReturnValueHandler() {
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (isRestController(returnType) || (isController(returnType) && isResponseBody(returnType))) && isAutoResult(returnType);
    }

    /**
     * 处理返回响应数据，输出日志，并响应数据
     * @Author wangwei
     * @Date 2024/5/11
     */
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(HttpServletRequest.class);
        mavContainer.setRequestHandled(true);
        HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse(HttpServletResponse.class);
        if(isMultipart(response.getContentType())) {
            return;
        }

        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        StringBuilder info = (StringBuilder) request.getAttribute(RequestContext.REQUEST_LOG_INFO);
        if (info == null) {
            info = new StringBuilder();
        }

        String requestId = (String) request.getAttribute(RequestContext.REQUEST_ID);
        if (returnValue instanceof Map) {
            if (isResponseResult((Map) returnValue)) {
                Map result = (Map) returnValue;
                result.put("requestId", requestId);
                writeResponse(request, response, info, returnValue);
                return;
            }
        }
        if (returnValue instanceof ResponseResult) {
            ((ResponseResult<?>) returnValue).setRequestId(requestId);
            writeResponse(request, response, info, returnValue);
            return;
        }
        ResponseResult<Object> result = new ResponseResult<>();
        result.setData(returnValue);
        result.setRequestId(requestId);
        writeResponse(request, response, info, result);
    }

    /**
     * 打印日志，并将结果输出到流
     * @Author wangwei
     * @Date 2024/5/11
     */
    private void writeResponse(HttpServletRequest request, HttpServletResponse response, StringBuilder loggerInfo, Object result) throws Exception {
        String jsonString = JSON.toJSONString(result, new SerializerFeature[]{SerializerFeature.WriteDateUseDateFormat});
        Long startTime = (Long) request.getAttribute(RequestContext.getRequestId());
        loggerInfo.append("[==响应结果=======]>: ").append(jsonString);
        loggerInfo.append("\n");
        if (startTime != null) {
            loggerInfo.append("[==执行耗时=======]>: ").append(System.currentTimeMillis() - startTime).append("ms").append("\n");
        }
        logger.info(loggerInfo.toString());
        response.getWriter().append(jsonString);
    }

    private boolean isRestController(MethodParameter returnType) {
        RestController annotation = (RestController) returnType.getDeclaringClass().getAnnotation(RestController.class);
        return annotation != null;
    }

    private boolean isController(MethodParameter returnType) {
        Controller annotation = returnType.getDeclaringClass().getAnnotation(Controller.class);
        return annotation != null;
    }

    private boolean isResponseBody(MethodParameter returnType) {
        ResponseBody methodAnnotation = (ResponseBody) returnType.getMethodAnnotation(ResponseBody.class);
        return methodAnnotation != null;
    }

    private boolean isAutoResult(MethodParameter returnType) {
        AutoResult methodAnnotation = (AutoResult) returnType.getMethodAnnotation(AutoResult.class);
        if (methodAnnotation != null) {
            return methodAnnotation.value();
        } else {
            AutoResult annotation = (AutoResult) returnType.getDeclaringClass().getAnnotation(AutoResult.class);
            return annotation != null && annotation.value();
        }
    }

    private boolean isResponseResult(Map body) {
        return body.containsKey("errorMsg") && body.containsKey("errorCode");
    }

    /**
     * 判断是否文件返回
     * @param contentType
     * @return
     */
    private boolean isMultipart(String contentType) {
        if(contentType == null) {
            return false;
        }
        contentType = contentType.toLowerCase();

        return contentType.contains("multipart") || contentType.contains("octet-stream") || contentType.contains("excel");
    }
}
